use serde::Serialize;
use thiserror::Error;

pub type IoErr = std::io::Error;

#[derive(Debug, Error)]
pub enum AppErr {
    #[error("io:{0}")]
    Io(#[from] IoErr),

    #[error("{0}")]
    Wrap(String),

    #[error("{0}")]
    Proto(String),
}

pub fn new_wrap_err<T>(msg: String) -> Result<T, AppErr> {
    Err(AppErr::Wrap(msg))
}

pub fn new_proto_err<T>(msg: String) -> Result<T, AppErr> {
    Err(AppErr::Proto(msg))
}

pub trait AppErrWrap<T> {
    fn wrap_err(self) -> Result<T, AppErr>;
}

impl<T, E: std::error::Error> AppErrWrap<T> for Result<T, E> {
    fn wrap_err(self) -> Result<T, AppErr> {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(AppErr::Wrap(e.to_string())),
        }
    }
}

impl <T> AppErrWrap<T> for Option<T> {

    fn wrap_err(self) -> Result<T, AppErr> {
        match self {
            Some(v) => Ok(v),
            None => Err(AppErr::Wrap(format!("wrap none")))
        }
    }
}

impl Serialize for AppErr {

    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer {
        let msg = self.to_string();
        serializer.serialize_str(&msg)
    }
}
